package com.answer.controller;

import com.answer.annotation.AuthCheck;
import com.answer.common.BaseResponse;
import com.answer.common.DeleteRequest;
import com.answer.common.ErrorCode;
import com.answer.constant.UserConstant;
import com.answer.entity.User;
import com.answer.exception.BusinessException;
import com.answer.model.dto.user.*;
import com.answer.model.vo.LoginUserVO;
import com.answer.model.vo.UserVO;
import com.answer.service.UserService;
import com.answer.utils.ResultUtils;
import com.answer.utils.ThrowUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;

import java.util.List;

import static com.answer.service.impl.UserServiceImpl.SALT;

/**
 * 用户接口
 */
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

    @Resource
    private UserService userService;

    /**
     * 用户注册接口
     * 该方法接收一个用户注册请求体，执行用户注册逻辑，并返回注册结果
     *
     * @param userRegisterRequest 用户注册请求体，包含用户账号、密码和确认密码等信息
     * @return 包含注册结果的响应对象，结果为用户ID
     * @throws BusinessException 如果入参为空，抛出业务异常，错误码为参数错误
     *                           如果用户账号、密码或确认密码为空，返回空的响应对象
     *                           如果注册成功，返回包含用户ID的响应对象
     */
    @PostMapping("/register")
    public BaseResponse<Long> userRegister(@RequestBody UserRegisterRequest userRegisterRequest) {
        // 检查请求对象是否为空
        if (userRegisterRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 提取用户账号、密码和确认密码
        String userAccount = userRegisterRequest.getUserAccount();
        String userPassword = userRegisterRequest.getUserPassword();
        String checkPassword = userRegisterRequest.getCheckPassword();
        // 检查用户账号、密码和确认密码是否为空
        if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword)) {
            return null;
        }
        // 调用用户服务的注册方法进行注册，并获取注册结果（用户ID）
        long result = userService.userRegister(userAccount, userPassword, checkPassword);
        // 返回注册成功的结果，包含用户ID
        return ResultUtils.success(result);
    }

    /**
     * 用户登录接口
     * 处理用户登录请求，验证用户提供的账户和密码，返回登录结果
     *
     * @param userLoginRequest 用户登录信息，包含用户账户和密码
     * @param request          HTTP请求对象，用于获取请求信息
     * @return 返回一个基础响应对象，包含登录用户的相关信息
     * 抛出异常：
     * - 当用户登录信息为空时，抛出业务异常，错误码为PARAMS_ERROR
     */
    @PostMapping("/login")
    public BaseResponse<LoginUserVO> userLogin(@RequestBody UserLoginRequest userLoginRequest, HttpServletRequest request) {
        // 检查用户登录信息是否为空
        if (userLoginRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 获取用户账户和密码
        String userAccount = userLoginRequest.getUserAccount();
        String userPassword = userLoginRequest.getUserPassword();
        // 检查用户账户和密码是否为空
        if (StringUtils.isAnyBlank(userAccount, userPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        LoginUserVO loginUserVO = userService.userLogin(userAccount, userPassword, request);
        return ResultUtils.success(loginUserVO);
    }

    /**
     * 用户登出
     * <p>
     * 此方法处理用户登出请求它首先检查传入的HttpServletRequest对象是否为空，
     * 如果为空，则抛出业务异常然后，它调用userService中的userLogout方法来处理登出逻辑，
     * 并将结果包装在BaseResponse对象中返回
     *
     * @param request HttpServletRequest对象，包含用户会话信息
     * @return 包含登出结果的BaseResponse对象，结果为布尔值，表示登出是否成功
     * @throws BusinessException 如果请求对象为空，抛出业务异常
     */
    @PostMapping("/logout")
    public BaseResponse<Boolean> userLogout(HttpServletRequest request) {
        // 检查请求对象是否为空，如果为空则抛出业务异常
        if (request == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 调用userService中的方法处理登出逻辑，返回登出结果
        boolean result = userService.userLogout(request);
        // 将登出结果包装在成功响应中并返回
        return ResultUtils.success(result);
    }

    /**
     * 处理获取登录用户信息的请求
     * <p>
     * 此方法通过GET请求处理"/get/login"路径，用于返回当前登录用户的信息
     * 它委托用户服务（userService）来获取登录用户，并将用户信息封装成一个响应对象（BaseResponse）返回
     *
     * @param request HTTP请求对象，用于提取请求信息
     * @return 包含登录用户信息的响应对象（BaseResponse<LoginUserVO>），其中LoginUserVO是用户信息的视图对象
     */
    @GetMapping("/get/login")
    public BaseResponse<LoginUserVO> getLoginUser(HttpServletRequest request) {
        User user = userService.getLoginUser(request);
        return ResultUtils.success(userService.getLoginUserVO(user));
    }

    /**
     * 使用PostMapping注解，指定处理添加用户的POST请求，请求路径为/add
     * 使用AuthCheck注解，指定当前操作需要ADMIN角色权限
     *
     * @param userAddRequest 从前端请求体中获取的用户添加信息
     * @return 返回一个BaseResponse对象，其中包含本次操作的结果信息和用户ID
     * 如果操作成功，返回用户ID；如果操作失败，返回相应的错误码和信息
     */
    @PostMapping("/add")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Long> addUser(@RequestBody UserAddRequest userAddRequest) {
        // 检查传入的用户添加信息是否为空，如果为空则抛出业务异常，错误码为参数错误
        if (userAddRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 创建一个新的User对象，用于存储经过处理的用户信息
        User user = new User();
        // 使用BeanUtils工具类将userAddRequest对象的属性复制到user对象中
        BeanUtils.copyProperties(userAddRequest, user);
        // 设置默认密码为"12345678"
        String defaultPassword = "12345678";
        // 对默认密码进行MD5加密，并将加密后的密码设置给用户对象
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + defaultPassword).getBytes());
        user.setUserPassword(encryptPassword);
        // 调用userService的save方法尝试保存用户，返回保存结果
        boolean result = userService.save(user);
        // 如果保存结果为false，则抛出业务异常，错误码为操作错误
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        // 操作成功，返回成功响应，包含用户ID
        return ResultUtils.success(user.getId());
    }

    /**
     * 删除用户接口
     * 该接口仅允许管理员角色访问，用于删除指定的用户
     *
     * @param deleteRequest 删除请求体，包含需要删除的用户ID
     * @return 响应对象，包含删除操作的结果
     * 如果用户ID无效（小于等于0），将抛出业务异常；
     * 否则，根据用户ID执行删除操作，并返回删除成功与否的结果
     */
    @PostMapping("/delete")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Boolean> deleteUser(@RequestBody DeleteRequest deleteRequest) {
        // 校验删除请求是否有效
        if (deleteRequest == null || deleteRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 执行用户删除操作
        boolean b = userService.removeById(deleteRequest.getId());
        // 返回删除结果
        return ResultUtils.success(b);
    }

    /**
     * 更新用户信息
     * 此方法用于更新现有用户的信息它首先检查请求体中的用户信息是否有效，
     * 然后将请求体中的用户信息更新到数据库中
     *
     * @param userUpdateRequest 包含待更新用户信息的请求体
     * @return 返回一个BaseResponse对象，其中包含一个Boolean值，指示更新操作是否成功
     * @throws BusinessException 如果用户信息为空或id为空，则抛出业务异常
     */
    @PostMapping("/update")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Boolean> updateUser(@RequestBody UserUpdateRequest userUpdateRequest) {
        // 检查用户信息和ID是否为空，如果为空则抛出业务异常
        if (userUpdateRequest == null || userUpdateRequest.getId() == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 创建一个新的User对象，用于存储更新后的用户信息
        User user = new User();
        // 将请求体中的用户信息复制到User对象中
        BeanUtils.copyProperties(userUpdateRequest, user);
        // 调用userService的updateById方法更新数据库中的用户信息
        boolean result = userService.updateById(user);
        // 如果更新失败，则抛出操作异常
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        // 返回成功响应，携带Boolean值true，表示更新成功
        return ResultUtils.success(true);
    }

    /**
     * 通过用户ID获取用户信息: 需要用户具备管理员角色
     *
     * @param id 用户ID，用于标识需要获取的用户
     * @return 返回一个基础响应对象，其中包含获取到的用户信息
     * <p>
     * 说明：
     * - @AuthCheck注解指定需要用户具备UserConstant.ADMIN_ROLE（管理员角色）才能访问此方法
     * - 如果传入的用户ID小于等于0，抛出BusinessException异常，错误码为参数错误（ErrorCode.PARAMS_ERROR）
     * - 使用userService.getById方法根据ID获取用户信息
     * - 如果用户不存在（user对象为null），使用ThrowUtils抛出异常，错误码为未找到错误（ErrorCode.NOT_FOUND_ERROR）
     * - 如果用户存在，使用ResultUtils构建一个成功的响应对象，并将用户信息作为响应数据返回
     */
    @GetMapping("/get")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<User> getUserById(long id) {
        if (id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        User user = userService.getById(id);
        ThrowUtils.throwIf(user == null, ErrorCode.NOT_FOUND_ERROR);
        return ResultUtils.success(user);
    }

    /**
     * 根据ID获取用户视图对象
     * 该方法接收一个用户ID和HttpServletRequest对象，返回一个封装了用户视图对象（UserVO）的BaseResponse对象
     * 主要用于提供给前端一个获取特定用户信息的接口，返回的信息包含用户的基本视图信息
     *
     * @param id 用户ID，用于标识需要查询的用户
     * @return 返回一个BaseResponse对象，其中包含用户视图对象（UserVO）
     */
    @GetMapping("/get/vo")
    public BaseResponse<UserVO> getUserVOById(long id) {
        // 根据ID获取用户信息，返回一个封装了用户对象的BaseResponse对象
        BaseResponse<User> response = getUserById(id);
        // 提取用户对象
        User user = response.getData();
        // 返回成功响应，其中包含转换后的用户视图对象
        return ResultUtils.success(userService.getUserVO(user));
    }

    /**
     * 分页查询用户列表
     * 通过分页和查询条件来获取用户列表，仅限管理员角色访问
     *
     * @param userQueryRequest 用户查询请求体，包含分页信息和查询条件
     * @return 分页的用户列表，包含用户数据和分页信息
     */
    @PostMapping("/list/page")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Page<User>> listUserByPage(@RequestBody UserQueryRequest userQueryRequest) {
        // 获取请求中的当前页码和每页大小
        long current = userQueryRequest.getCurrent();
        long size = userQueryRequest.getPageSize();
        // 创建分页对象并执行查询
        Page<User> userPage = userService.page(new Page<>(current, size),
                userService.getQueryWrapper(userQueryRequest));
        // 返回成功响应，携带分页用户列表
        return ResultUtils.success(userPage);
    }

    /**
     * 根据分页查询请求获取用户列表
     *
     * @param userQueryRequest 用户查询请求体
     * @return 包含用户列表的分页数据响应
     *
     * 说明：本方法通过接收一个用户查询请求体和HTTP请求对象，来返回一个包含用户列表的分页数据响应。
     *       方法首先校验请求体是否为空，为空则抛出业务异常。然后提取出分页参数，并进行爬虫行为限制检查。
     *       随后执行分页查询操作，将查询结果转换为指定的视图对象，并设置到分页数据对象中，最后返回成功响应。
     */
    @PostMapping("/list/page/vo")
    public BaseResponse<Page<UserVO>> listUserVOByPage(@RequestBody UserQueryRequest userQueryRequest) {
        // 检查请求体是否为空，为空则抛出参数错误异常
        if (userQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 提取当前页码和页面大小
        long current = userQueryRequest.getCurrent();
        long size = userQueryRequest.getPageSize();
        // 限制单页数据量，防止爬虫行为
        ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR);
        // 执行分页查询，获取用户列表
        Page<User> userPage = userService.page(new Page<>(current, size),
                userService.getQueryWrapper(userQueryRequest));
        // 初始化分页视图对象
        Page<UserVO> userVOPage = new Page<>(current, size, userPage.getTotal());
        // 将用户实体列表转换为用户视图对象列表
        List<UserVO> userVO = userService.getUserVO(userPage.getRecords());
        // 设置用户视图对象列表到分页数据对象中
        userVOPage.setRecords(userVO);
        // 返回成功响应，包含用户列表的分页数据
        return ResultUtils.success(userVOPage);
    }

    /**
     * 更新当前用户信息
     * 此方法允许用户更新自己的信息它首先校验传入的请求对象是否为空，
     * 然后从请求中获取当前登录的用户信息，将请求中的用户信息更新到一个新的User对象中，
     * 并设置此对象的ID为当前登录用户的ID最后，它尝试更新数据库中的用户信息，
     * 如果更新成功，则返回一个表示成功的响应对象
     *
     * @param userUpdateMyRequest 包含用户更新信息的请求对象
     * @param request HTTP请求对象，用于获取当前会话信息
     * @return 包含更新操作结果的响应对象
     * @throws BusinessException 如果请求参数为空，则抛出业务异常
     */
    @PostMapping("/update/my")
    public BaseResponse<Boolean> updateMyUser(@RequestBody UserUpdateMyRequest userUpdateMyRequest,
                                                  HttpServletRequest request) {
        // 检查请求对象是否为空
        if (userUpdateMyRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 获取当前登录用户
        User loginUser = userService.getLoginUser(request);
        // 创建一个新的User对象，用于存储更新后的用户信息
        User user = new User();
        // 将请求中的用户信息复制到新的User对象中
        BeanUtils.copyProperties(userUpdateMyRequest, user);
        // 设置用户ID为当前登录用户的ID
        user.setId(loginUser.getId());
        // 尝试更新数据库中的用户信息
        boolean result = userService.updateById(user);
        // 如果更新失败，抛出操作异常
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
        // 返回一个表示成功的响应对象
        return ResultUtils.success(true);
    }
}
